<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>未来之窗 - 文章向量搜索</title>
    <style>
        :root {
            --primary: #3a7bd5;
            --secondary: #6d8cb5;
            --light: #f5f7fa;
            --dark: #2c3e50;
            --success: #2ecc71;
            --warning: #e67e22;
            --danger: #e74c3c;
        }
        
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
            font-family: 'Microsoft YaHei', sans-serif;
        }
        
        body {
            background-color: var(--light);
            color: var(--dark);
            line-height: 1.6;
            padding: 20px;
        }
        
        header {
            text-align: center;
            padding: 30px 0;
            background: linear-gradient(135deg, var(--primary), var(--secondary));
            color: white;
            border-radius: 8px;
            margin-bottom: 30px;
            box-shadow: 0 4px 12px rgba(0,0,0,0.1);
        }
        
        h1 {
            font-size: 2.2rem;
            margin-bottom: 10px;
        }
        
        .subtitle {
            font-size: 1.1rem;
            opacity: 0.9;
            max-width: 700px;
            margin: 0 auto;
        }
        
        .container {
            max-width: 1200px;
            margin: 0 auto;
            display: grid;
            grid-template-columns: 350px 1fr;
            gap: 30px;
        }
        
        @media (max-width: 900px) {
            .container {
                grid-template-columns: 1fr;
            }
        }
        
        .card {
            background: white;
            border-radius: 8px;
            padding: 20px;
            box-shadow: 0 2px 8px rgba(0,0,0,0.08);
            margin-bottom: 30px;
        }
        
        .card h2 {
            color: var(--primary);
            margin-bottom: 15px;
            padding-bottom: 10px;
            border-bottom: 2px solid #f1f1f1;
        }
        
        .search-group {
            margin-bottom: 20px;
        }
        
        label {
            display: block;
            margin-bottom: 8px;
            font-weight: 500;
        }
        
        input, textarea, select {
            width: 100%;
            padding: 10px 12px;
            border: 1px solid #ddd;
            border-radius: 4px;
            font-size: 1rem;
            margin-bottom: 15px;
        }
        
        textarea {
            min-height: 100px;
            resize: vertical;
        }
        
        button {
            background-color: var(--primary);
            color: white;
            border: none;
            padding: 10px 20px;
            border-radius: 4px;
            cursor: pointer;
            font-size: 1rem;
            transition: background-color 0.2s;
        }
        
        button:hover {
            background-color: #2d62b3;
        }
        
        button.secondary {
            background-color: var(--secondary);
        }
        
        button.success {
            background-color: var(--success);
        }
        
        .button-group {
            display: flex;
            gap: 10px;
            margin-bottom: 20px;
            flex-wrap: wrap;
        }
        
        .results-section {
            margin-top: 20px;
        }
        
        .result-item {
            border: 1px solid #eee;
            border-radius: 6px;
            padding: 15px;
            margin-bottom: 15px;
            transition: transform 0.2s, box-shadow 0.2s;
        }
        
        .result-item:hover {
            transform: translateY(-2px);
            box-shadow: 0 3px 10px rgba(0,0,0,0.08);
        }
        
        .result-header {
            display: flex;
            justify-content: space-between;
            align-items: center;
            margin-bottom: 10px;
        }
        
        .result-title {
            font-weight: bold;
            color: var(--primary);
            font-size: 1.1rem;
        }
        
        .similarity-score {
            padding: 4px 10px;
            border-radius: 20px;
            font-size: 0.9rem;
            font-weight: bold;
        }
        
        .high {
            background-color: rgba(46, 204, 113, 0.15);
            color: var(--success);
        }
        
        .medium {
            background-color: rgba(230, 126, 34, 0.15);
            color: var(--warning);
        }
        
        .low {
            background-color: rgba(231, 76, 60, 0.15);
            color: var(--danger);
        }
        
        .result-content {
            color: #555;
            font-size: 0.95rem;
            margin-bottom: 10px;
            display: -webkit-box;
            -webkit-line-clamp: 3;
            -webkit-box-orient: vertical;
            overflow: hidden;
        }
        
        .vocab-list {
            max-height: 200px;
            overflow-y: auto;
            border: 1px solid #eee;
            border-radius: 4px;
            margin-top: 15px;
        }
        
        .vocab-item {
            padding: 8px 12px;
            border-bottom: 1px solid #f5f5f5;
            display: flex;
            justify-content: space-between;
        }
        
        .vocab-item:last-child {
            border-bottom: none;
        }
        
        .status-bar {
            font-size: 0.9rem;
            color: #777;
            margin-top: 15px;
            padding-top: 10px;
            border-top: 1px dashed #eee;
        }
        
        .article-meta {
            font-size: 0.85rem;
            color: #888;
        }
    </style>
</head>
<body>
    <header>
        <h1>未来之窗 - 文章向量搜索</h1>
        <p class="subtitle">基于向量余弦相似度的智能文章内容搜索工具，快速找到最相关的文章</p>
    </header>
    
    <div class="container">
        <div class="sidebar">
            <div class="card">
                <h2>搜索设置</h2>
                <div class="search-group">
                    <label for="searchQuery">搜索关键词或句子</label>
                    <textarea id="searchQuery" placeholder="输入你想搜索的内容...例如：人工智能的发展趋势"></textarea>
                </div>
                
                <div class="search-group">
                    <label for="resultCount">结果数量</label>
                    <select id="resultCount">
                        <option value="5">5条</option>
                        <option value="10" selected>10条</option>
                        <option value="15">15条</option>
                        <option value="20">20条</option>
                    </select>
                </div>
                
                <div class="button-group">
                    <button id="searchBtn">开始搜索</button>
                    <button id="clearBtn" class="secondary">清空</button>
                </div>
                
                <div class="status-bar">
                    <div>当前文章库: <span id="articleCount">0</span> 篇</div>
                    <div>词汇表大小: <span id="vocabCount">0</span> 个</div>
                </div>
            </div>
            
            <div class="card">
                <h2>词汇管理</h2>
                <div class="search-group">
                    <label for="wordInput">添加关键词</label>
                    <input type="text" id="wordInput" placeholder="输入要添加的关键词...">
                    <button id="addWordBtn" class="success">添加词汇</button>
                </div>
                
                <div>
                    <h3>常用词汇</h3>
                    <div class="vocab-list" id="vocabList"></div>
                </div>
            </div>
        </div>
        
        <div class="main-content">
            <div class="card">
                <h2>搜索结果</h2>
                <div id="searchStatus" style="text-align:center; padding:30px 0; color:#777;">
                    请输入搜索内容并点击"开始搜索"
                </div>
                <div class="results-section" id="resultsContainer"></div>
            </div>
            
            <div class="card">
                <h2>文章库管理</h2>
                <div class="search-group">
                    <label for="articleTitle">文章标题</label>
                    <input type="text" id="articleTitle" placeholder="输入文章标题">
                    
                    <label for="articleContent">文章内容</label>
                    <textarea id="articleContent" placeholder="输入文章内容"></textarea>
                    
                    <label for="articleVector">文章向量 (JSON数组)</label>
                    <textarea id="articleVector" placeholder="[0.12, 0.34, 0.56, ...]"></textarea>
                </div>
                
                <div class="button-group">
                    <button id="addArticleBtn" class="success">添加文章</button>
                    <button id="exportBtn" class="secondary">导出文章库</button>
                </div>
            </div>
        </div>
    </div>

    <script>
        // 词汇表模块
        const Vocabulary = (() => {
            class Vocabulary {
                constructor() {
                    this.words = new Map(); // 词 -> 索引
                    this.index = 0;
                }
                
                // 添加词汇
                addWord(word) {
                    const processed = word.trim().toLowerCase();
                    if (!processed || this.words.has(processed)) return false;
                    
                    this.words.set(processed, this.index++);
                    return true;
                }
                
                // 获取词汇索引
                getWordIndex(word) {
                    return this.words.get(word.trim().toLowerCase()) ?? null;
                }
                
                // 获取词汇数量
                getSize() {
                    return this.index;
                }
                
                // 获取所有词汇
                getAllWords() {
                    return Array.from(this.words.keys());
                }
                
                // 导出为JSON
                toJSON() {
                    return {
                        words: Object.fromEntries(this.words),
                        index: this.index
                    };
                }
                
                // 从JSON导入
                fromJSON(data) {
                    this.words = new Map(Object.entries(data.words));
                    this.index = data.index;
                }
            }
            
            return Vocabulary;
        })();
        
        // 向量计算模块
        const VectorMath = (() => {
            class VectorMath {
                // 计算点积
                static dotProduct(vec1, vec2) {
                    if (vec1.length !== vec2.length) throw new Error("向量长度不匹配");
                    
                    let product = 0;
                    for (let i = 0; i < vec1.length; i++) {
                        product += vec1[i] * vec2[i];
                    }
                    return product;
                }
                
                // 计算向量模长
                static magnitude(vec) {
                    let sum = 0;
                    for (let i = 0; i < vec.length; i++) {
                        sum += vec[i]** 2;
                    }
                    return Math.sqrt(sum);
                }
                
                // 计算余弦相似度
                static cosineSimilarity(vec1, vec2) {
                    const dot = this.dotProduct(vec1, vec2);
                    const mag1 = this.magnitude(vec1);
                    const mag2 = this.magnitude(vec2);
                    
                    if (mag1 === 0 || mag2 === 0) return 0;
                    
                    return parseFloat((dot / (mag1 * mag2)).toFixed(6));
                }
            }
            
            return VectorMath;
        })();
        
        // 文章库模块
        const ArticleStore = (() => {
            class ArticleStore {
                constructor() {
                    this.articles = []; // {id, title, content, vector}
                    this.nextId = 1;
                }
                
                // 添加文章
                addArticle(title, content, vector) {
                    if (!title || !vector || !Array.isArray(vector)) return null;
                    
                    const id = `art-${this.nextId++}`;
                    this.articles.push({
                        id,
                        title,
                        content: content || "",
                        vector: vector.map(v => parseFloat(v.toFixed(6)))
                    });
                    
                    return id;
                }
                
                // 获取所有文章
                getAllArticles() {
                    return [...this.articles];
                }
                
                // 根据ID获取文章
                getArticleById(id) {
                    return this.articles.find(a => a.id === id) || null;
                }
                
                // 搜索相似文章
                searchSimilar(vector, topN = 10) {
                    const results = this.articles.map(article => ({
                        article,
                        similarity: VectorMath.cosineSimilarity(vector, article.vector)
                    }));
                    
                    // 按相似度排序并返回前N条
                    return results.sort((a, b) => b.similarity - a.similarity)
                                  .slice(0, topN);
                }
                
                // 获取文章数量
                getCount() {
                    return this.articles.length;
                }
                
                // 导出为JSON
                toJSON() {
                    return this.articles;
                }
                
                // 从JSON导入
                fromJSON(data) {
                    this.articles = data;
                    this.nextId = Math.max(...this.articles.map(a => parseInt(a.id.split('-')[1])), 0) + 1;
                }
            }
            
            return ArticleStore;
        })();
        
        // 生成演示数据
        function generateDemoData() {
            // 主题分类
            const topics = [
                { name: "人工智能", keywords: ["人工智能", "机器学习", "深度学习", "神经网络", "算法"] },
                { name: "量子计算", keywords: ["量子", "量子计算", "量子比特", "叠加态", "纠缠"] },
                { name: "生物医药", keywords: ["生物", "医学", "基因", "药物", "治疗"] },
                { name: "新能源", keywords: ["能源", "电池", "光伏", "可再生", "环保"] },
                { name: "太空探索", keywords: ["太空", "宇宙", "航天", "星球", "探测器"] },
                { name: "区块链", keywords: ["区块链", "加密", "数字货币", "分布式", "账本"] }
            ];
            
           // 生成演示演示词汇表
const vocab = new Vocabulary ();
topics.forEach (topic => {
topic.keywords.forEach (word => vocab.addWord (word));
});

// 生成 30 篇演示文章
var articles= [];
const vectorSize = vocab.getSize ();

for (let i = 0; i < 30; i++) {
// 随机选择主题
const topic = topics [Math.floor (Math.random () * topics.length)];
const topicIndex = topics.indexOf (topic);

// 生成向量 (主题相关词权重更高)
const vector = [];
for (let j = 0; j < vectorSize; j++) {
// 主题相关词权重更高
let weight = Math.random () * 0.5;

// 为当前主题的关键词增加权重
if (j < topic.keywords.length && topicIndex === Math.floor (j / (vectorSize /topics.length))) {
weight += 0.5 + Math.random () * 0.5;
}

vector.push(parseFloat(weight.toFixed(4)));
}

// 生成文章内容
const content = `${topic.name}技术的最新发展。${topic.keywords[0]}在${topic.keywords[1]}领域的应用取得了突破，研究人员利用${topic.keywords[2]}方法实现了${Math.floor(Math.random() * 100)}%的性能提升。这一进展将对${topic.keywords[3]}行业产生深远影响，预计在未来${Math.floor(Math.random() * 5) + 1}年内得到广泛应用。相关专家表示，这一技术突破将推动整个行业的变革，为${topic.keywords[4]}领域带来新的发展机遇。`;

articles.push({
title: `${topic.name}研究进展 (演示文章${i+1})`,
content: content,
vector: vector
});
}

     return { vocab, articles };
}
</SCRIPT>